package dedup

import (
	"io"
)

// Chunker interface for different chunking algorithms
type Chunker interface {
	Chunk(reader io.Reader) ([][]byte, error)
}

// FixedChunker splits data into fixed-size chunks
type FixedChunker struct {
	chunkSize int64
}

func NewFixedChunker(size int64) *FixedChunker {
	return &FixedChunker{chunkSize: size}
}

func (c *FixedChunker) Chunk(reader io.Reader) ([][]byte, error) {
	var chunks [][]byte
	buffer := make([]byte, c.chunkSize)

	for {
		n, err := io.ReadFull(reader, buffer)
		if n > 0 {
			// Make a copy of the data
			chunk := make([]byte, n)
			copy(chunk, buffer[:n])
			chunks = append(chunks, chunk)
		}

		if err == io.EOF || err == io.ErrUnexpectedEOF {
			break
		}
		if err != nil {
			return nil, err
		}
	}

	return chunks, nil
}

// CDCChunker implements Content-Defined Chunking (FastCDC)
type CDCChunker struct {
	avgSize int64
	minSize int64
	maxSize int64
	mask    uint64
}

func NewCDCChunker(avgSize int64) *CDCChunker {
	return &CDCChunker{
		avgSize: avgSize,
		minSize: avgSize / 2,
		maxSize: avgSize * 2,
		mask:    calculateMask(avgSize),
	}
}

func (c *CDCChunker) Chunk(reader io.Reader) ([][]byte, error) {
	var chunks [][]byte

	data, err := io.ReadAll(reader)
	if err != nil {
		return nil, err
	}

	pos := 0
	for pos < len(data) {
		boundary := c.findBoundary(data[pos:])
		if boundary == -1 {
			boundary = len(data) - pos
		}

		chunk := make([]byte, boundary)
		copy(chunk, data[pos:pos+boundary])
		chunks = append(chunks, chunk)
		pos += boundary
	}

	return chunks, nil
}

func (c *CDCChunker) findBoundary(data []byte) int {
	if len(data) <= int(c.minSize) {
		return len(data)
	}

	// Use rolling hash to find content-defined boundary
	hash := uint64(0)

	// Start from minSize
	for i := int(c.minSize); i < len(data) && i < int(c.maxSize); i++ {
		// Simple rolling hash (Rabin fingerprint simulation)
		hash = (hash << 1) + uint64(data[i])

		// Check if this is a boundary
		if (hash & c.mask) == 0 {
			return i + 1
		}
	}

	// Return max size if no boundary found
	if len(data) >= int(c.maxSize) {
		return int(c.maxSize)
	}
	return len(data)
}

func calculateMask(avgSize int64) uint64 {
	// Calculate mask for target average chunk size
	// More bits set = smaller chunks
	bits := 0
	for (1 << bits) < avgSize {
		bits++
	}
	return (1 << bits) - 1
}
